package org.itsolutions.javaBackup.bootstrapping;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.itsolutions.javaBackup.core.properties.CoreProperty;
import org.itsolutions.javaBackup.exceptions.AException;
import org.itsolutions.javaBackup.exceptions.CoreException;
import org.itsolutions.javaBackup.ioc.AJavaBackupModule;
import org.itsolutions.javaBackup.logger.ELogLevel;
import org.itsolutions.javaBackup.logger.ILogger;
import org.itsolutions.javaBackup.logger.LoggerHelper;
import org.itsolutions.javaBackup.spi.EModule;
import org.itsolutions.javaBackup.spi.ENodeFunctionality;

/**
 * 
 * @author Tomas Fecko
 */
public final class ModuleLoader {

	private static final ILogger 	LOGGER = LoggerHelper.getLogger(ModuleLoader.class);
	private EnumSet<EModule>		mModules;

	/**
	 * @return booted modules modules
	 */
	public EnumSet<EModule> getBootedModules() {
		//TODO - if the booting will be called during runtime (not just once after start), this can cause concurrency issues
		return mModules;
	}

	/**
	 * 
	 * @param pModule module to check if it's loaded
	 * @return true if module is loaded
	 */
	public boolean isModuleLoaded(EModule pModule) {
		return mModules.contains(pModule);
	}

	/**
	 * 
	 * @return module instances
	 * @throws AException exception during the module instantiation
	 */
	public List<AJavaBackupModule> getModuleInstances() throws AException {
		List<Class<?>> moduleClasses = getModuleClasses();
		List<AJavaBackupModule> modules = new ArrayList<AJavaBackupModule>();
		for (Class<?> clazz : moduleClasses) {
			try {
				modules.add((AJavaBackupModule) clazz.newInstance());
			} catch (InstantiationException e) {
				LOGGER.log(ELogLevel.ERROR, e);
				throw new CoreException(e);
			} catch (IllegalAccessException e) {
				LOGGER.log(ELogLevel.ERROR, e);
				throw new CoreException(e);
			}
		}
		return modules;
	}

	/**
	 * 
	 * @return loaded modules
	 * @throws AException any exception during the booting process
	 */
	private Set<EModule> getModules() throws AException {
		List<EModule> modulesFromProperties = CoreProperty.NODE_MODULES.get();
		Set<EModule> modules = new HashSet<EModule>();
		if (modulesFromProperties.isEmpty() || modulesFromProperties.get(0) == EModule.UNSPECIFIED) {
			List<ENodeFunctionality> nodeFunctionalities = CoreProperty.NODE_FUNCTIONALITY.get();
			for (ENodeFunctionality nodeFunctionality : nodeFunctionalities) {
				modules.addAll(nodeFunctionality.getRequieredModules());
			}
		} else {
			modules.addAll(modulesFromProperties);
		}
		mModules = EnumSet.copyOf(modules);
		return modules;
	}	

	/**
	 * 
	 * @return module class
	 * @throws AException any exception during the class retrieval
	 */
	private List<Class<?>> getModuleClasses() throws AException {
		Set<EModule> modules = getModules();
		List<Class<?>> returnList = new ArrayList<Class<?>>();
		for (EModule module : modules) {
			try {
				switch (module) {
				case FTP:
					returnList.add(getClass(CoreProperty.FTP_MODULE.get()));					
					break;
				case GUI_WEB:
					returnList.add(getClass(CoreProperty.GUI_WEB_MODULE.get()));					
					break;
				case GIT:
					returnList.add(getClass(CoreProperty.GIT_MODULE.get()));					
					break;
				case UNSPECIFIED:
				case CORE:
					break;
				default:
					LOGGER.log(ELogLevel.WARN, "Can't find module class for module:", module);
				}
			} catch (AException e) {
				throw e;
			}
		}
		return returnList;
	}

	/**
	 * 
	 * @param pClassName class name
	 * @return
	 */
	private Class<?> getClass(String pClassName) throws AException {
		try {
			LOGGER.log(ELogLevel.DEBUG, "Creating class for name:", pClassName);			
			return (Class<?>) Class.forName(pClassName);
		} catch (Exception e) {
			AException ex = new CoreException(e);
			LOGGER.log(ELogLevel.ERROR, e);
			throw ex;
		}
	}
}




